package org.l.mybatis.core.sqlsource;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.l.mybatis.core.annotation.SqlsourceInsert;
import org.l.mybatis.core.exception.MethodDefineException;
import org.l.mybatis.core.exception.SqlsourceBuilderException;
import org.l.mybatis.core.handler.FieldHandler;
import org.l.mybatis.core.handler.ObjectHandler;

/**
 * @author 阿旺
 * @version 2017年11月1日 下午9:22:11
 */
public class SqlsourceBuilderInsert extends SqlsourceBuilderAbstract implements SqlsourceBuilder {
    private SqlsourceInsert sqlsourceInsert;
    private boolean multiple;

    public SqlsourceBuilderInsert(Configuration configuration, Class<?> type, Method method,
            SqlsourceInsert sqlsourceInsert) throws MethodDefineException {
        super(configuration, type, method);
        this.sqlsourceInsert = sqlsourceInsert;
        analysis();
    }

    private void analysis() throws MethodDefineException {
        Class<?>[] paramTypes = this.method.getParameterTypes();
        this.paramCount = paramTypes.length;

        if (paramCount == 0) {
            throw new MethodDefineException("缺少表映射对象参数:" + method);
        } else if (paramCount == 1) {
            this.multiple = Collection.class.isAssignableFrom(paramTypes[0]);
        } else {
            throw new MethodDefineException("只能接受一个表映射对象参数:" + method);
        }

    }

    private final SqlCommandType sqlCommandType = SqlCommandType.INSERT;

    @Override
    public SqlCommandType getSqlCommandType() {
        return this.sqlCommandType;
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        List<ParameterMapping> parameterMappings = new ArrayList<>();
        String sql;

        if (multiple) {
            sql = createMultipleInsertSql(parameterObject, parameterMappings);
        } else {
            sql = createSimpleInsertSql(parameterObject, parameterMappings);
        }

        BoundSql boundSql = new BoundSql(this.configuration, sql, parameterMappings, parameterObject);
        return boundSql;
    }

    private String createMultipleInsertSql(Object parameterObject, List<ParameterMapping> parameterMappings) {
        MetaObject metaObject = SystemMetaObject.forObject(parameterObject);
        Collection<?> collect = (Collection<?>) metaObject.getValue("list");

        if (collect == null || collect.isEmpty()) {
            throw new SqlsourceBuilderException("插入空列表错误");
        }

        ObjectHandler objectHandler = null;

        for (Object object : collect) {
            objectHandler = ObjectHandler.create(object.getClass());
            break;
        }

        StringBuilder insert = new StringBuilder(10);
        insert.append("insert into ").append(objectHandler.getTableName()).append(" (");
        List<FieldHandler> insertFieldHandlers;

        if (sqlsourceInsert.fields().length > 0) {
            insertFieldHandlers = fieldHandles(objectHandler, sqlsourceInsert.fields());
        } else {

            if (this.sqlsourceInsert.ignorePrimary()) {
                insertFieldHandlers = objectHandler.getDbFields();
            } else {
                insertFieldHandlers = objectHandler.getAllFields();
            }
        }

        int fieldSize = 0;
        
        for (FieldHandler handler : insertFieldHandlers) {
            if (fieldSize > 0)
                insert.append(",");

            fieldSize++;
            insert.append(handler.getDbName());
        }
        
        insert.append(") values");
        int size = collect.size();

        for (int i = 0; i < size; i++) {
            if (i > 0)
                insert.append(",");

            insert.append("(").append(createPoint(fieldSize)).append(')');

            for (FieldHandler handler : insertFieldHandlers) {
                parameterMappings.add(createParameterMapping(handler, "list[" + i + "]"));
            }
        }
        
        return insert.toString();
    }

    /**
     * 创建单个实体插入的sql语句
     * @param parameterObject
     * @param parameterMappings
     * @return
     */
    private String createSimpleInsertSql(Object parameterObject, List<ParameterMapping> parameterMappings) {
        MetaObject metaObject = SystemMetaObject.forObject(parameterObject);
        Class<?> paramType = metaObject.getOriginalObject().getClass();
        ObjectHandler objectHandler = ObjectHandler.create(paramType);

        StringBuilder insert = new StringBuilder(10);
        insert.append("insert into ").append(objectHandler.getTableName()).append(" (");

        int fieldSize = this.sqlsourceInsert.fields().length;

        if (fieldSize > 0) {
            for (int i = 0; i < fieldSize; i++) {
                if (i > 0)
                    insert.append(",");

                String name = sqlsourceInsert.fields()[i];
                insert.append(name);
                FieldHandler handler = objectHandler.getFieldHandlerByDbField(name);
                parameterMappings.add(createParameterMapping(handler));
            }

        } else {
            fieldSize = 0;
            List<FieldHandler> fields;

            if (this.sqlsourceInsert.ignorePrimary()) {
                fields = objectHandler.getDbFields();
            } else {
                fields = objectHandler.getAllFields();
            }

            for (FieldHandler handler : fields) {
                if (this.sqlsourceInsert.ignoreNull()) {
                    Object value = metaObject.getValue(handler.getName());

                    if (value == null)
                        continue;
                }

                fieldSize++;

                if (fieldSize > 1)
                    insert.append(",");

                insert.append(handler.getDbName());
                parameterMappings.add(createParameterMapping(handler));
            }

        }

        insert.append(") values (").append(createPoint(fieldSize)).append(')');
        return insert.toString();
    }

    /**
     * 创建？占位符
     * @param size
     * @return
     */
    private String createPoint(int size) {
        StringBuilder points = new StringBuilder(size * 2 - 1);

        for (int i = 0; i < size; i++) {
            if (i > 0)
                points.append(',');

            points.append('?');
        }

        return points.toString();
    }

}
